package org.zn.note.mybatis.typehandler;

import org.apache.ibatis.executor.resultset.ResultSetWrapper;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * Mybatis 类型处理器
 * <p>
 * 一、注册自定义类型处理器  mybatis-config.xml <typeHandlers>
 * 方式一：配置文件中指定
 * <typeHandler jdbcType="VARCHAR" javaType="string" handler="org.zn.note.mybatis.typehandler.MyTypeHandler"/>
 * 方式二：指定扫描包 + 注解
 * <package name="org.zn.note.mybatis.typehandler"/>
 * 注解javaType @MappedTypes({String.class})
 * 注解jdbcType @MappedJdbcTypes(JdbcType.VARCHAR)
 * 这两种注册方式都可以，都是：① 实现TypeHandler ② 指定JavaType和JdbcType
 * (JavaType、JdbcType，都不是必需的，下面有说明)
 * <p>
 * 二、使用自定义类型处理器  XXXXMapper.xml
 * 输入（预处理传入参数）
 * where merchant_name = #{merchantName, jdbcType=VARCHAR, javaType=string, typeHandler=org.zn.note.mybatis.typehandler.MyTypeHandler}
 * 输出（查询返回的结果集）
 * <result column="merchant_name" property="merchantName" jdbcType="VARCHAR" javaType="string" typeHandler="org.zn.note.mybatis.typehandler.MyTypeHandler"/>
 * (jdbcType、javaType、typeHandler，都不是必需的，下面有说明)
 * <p>
 * 三、总结：
 * 1、在 Mapper.xml 里，无论输入(where参数)还是输出(结果集)，都有 jdbcType、javaType、typeHandler，这三个要素
 * 2、优先级最高，直接指定类型转换器(typeHandler)。
 * 如果在Mapper.xml里用typeHandler，则这个类型转换器不用注册！不用标注jdbcType！不用标注javaType！仅需要实现{@link TypeHandler}
 * 也就是指定类型转换器，跳过了javaType、jdbcType匹配的阶段。
 * 3、其次，根据 javaType + jdbcType匹配。
 * TypeHandlerMap = Map( javaType, Map( jdbcType, TypeHandler))
 * 一般情况，即使不指定javaType，MyBatis能自己判断；但是jdbcType判断不出来。
 * 比如上面的merchantName，可以判断出这个属性在java中是String的，但是在数据库是什么类型是判断不出来的。
 * 所以如果都不指定，比如 where merchant_name = #{merchantName}，则会匹配 Map(string, (null, typeHandler))
 * 4、最后，是根据 jdbcType匹配。
 * 一般情况，注册的handler都会带javaType，这会注册到 typeHandlerMap = Map(javaType, Map(jdbcType, handler))
 * 匹配的时候也是优先从这个map匹配。
 * 但注册时候也可以不指定javaType，仅有jdbcType，这时会注册到 jdbcTypeHandlerMap = Map(jdbcType, handler)
 * 当用 javaType+jdbcType 在 typeHandlerMap 里找不到的时候，就会仅用 jdbcType在jdbcTypeHandlerMap里找能用的handler ！
 * 参见 {@link ResultSetWrapper#getTypeHandler(Class, String)}
 * 5、MyBatis自带的一些TypeHandler，已经注册到了TypeHandlerMap，{@link org.apache.ibatis.type.TypeHandlerRegistry}
 * 自定义注册的TypeHandler，会根据 javaType+jdbcType来覆盖 TypeHandlerMap，或者根据jdbcType覆盖 jdbcTypeHandlerMap
 *
 * 枚举类型处理器
 * {@link org.apache.ibatis.type.EnumTypeHandler}
 * {@link org.apache.ibatis.type.EnumOrdinalTypeHandler}
 */
//@MappedTypes({String.class})
//@MappedJdbcTypes(JdbcType.VARCHAR)
public class MyTypeHandler implements TypeHandler<String> {
    @Override
    public void setParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
        System.out.printf("[自定义类型处理器] Execute MyTypeHandler.setParameter(%d)\n", i);
        ps.setString(i, parameter);
    }

    @Override
    public String getResult(ResultSet rs, String columnName) throws SQLException {
        System.out.printf("[自定义类型处理器] Execute MyTypeHandler.getResult(By columnName: %s)\n", columnName);
        return rs.getString(columnName);
    }

    @Override
    public String getResult(ResultSet rs, int columnIndex) throws SQLException {
        System.out.printf("[自定义类型处理器] Execute MyTypeHandler.getResult(By columnName: %d)\n", columnIndex);
        return rs.getString(columnIndex);
    }

    @Override
    public String getResult(CallableStatement cs, int columnIndex) throws SQLException {
        System.out.println("[自定义类型处理器] Execute MyTypeHandler.getResult(CallableStatement)");
        return cs.getString(columnIndex);
    }
}
